/**
 * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi_util.h"

void CallbackListener::OnCallback() const
{
    uint8_t length = 2;
    napi_value result[2] = {nullptr};
    napi_value callback = nullptr;
    napi_status ret;
    napi_get_undefined(env_, &result[Number::ZEROTH]);
    napi_handle_scope scope = nullptr;
    napi_open_handle_scope(env_, &scope);
    if (error.code != 0) {
        result[0] = CreateBusinessError(env_, error.code, error.message);
    }
    CreatePixelMap(env_, this->pixelMap, result[Number::FIRST]);
    ret = napi_get_reference_value(env_, callbackRef_, &callback);
    if (ret != napi_ok) {
        LOGE("napi_get_reference_value err");
    }
    ret = napi_call_function(env_, nullptr, callback, length, result, nullptr);
    if (ret != napi_ok) {
        LOGE("failed to call function, code:%{public}d", ret);
    }
    napi_close_handle_scope(env_, scope);
}

void CallbackListener::OnCallbackAsync() const
{
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    if (!loop) {
        LOGE("failed to get event loop");
        return;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (!work) {
        LOGE("failed to allocate work");
        return;
    }
    std::unique_ptr<CallbackInfo> callbackInfo =
        std::make_unique<CallbackInfo>(this);
    work->data = callbackInfo.get();
    int ret = uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            CallbackInfo *callbackInfo =
                reinterpret_cast<CallbackInfo *>(work->data);
            if (callbackInfo) {
                callbackInfo->listener_->OnCallback();
                delete callbackInfo;
            }
            delete work;
        });
    if (ret) {
        LOGE("failed to execute work");
        delete work;
    } else {
        callbackInfo.release();
    }
}

bool IsMatchType(const napi_env &env, const napi_value &value,
                 const napi_valuetype &type)
{
    napi_valuetype paramType = napi_undefined;
    napi_status ret = napi_typeof(env, value, &paramType);
    if ((ret != napi_ok) || (paramType != type)) {
        LOGE("Type mismatch");
        return false;
    }
    return true;
}

bool GetStringValue(const napi_env &env, const napi_value &value,
                    std::string &result)
{
    if (!IsMatchType(env, value, napi_string)) {
        return false;
    }
    size_t bufLength = 0;
    napi_status ret =
        napi_get_value_string_utf8(env, value, nullptr, 0, &bufLength);
    if (ret != napi_ok) {
        LOGE("napi_get_value_string_utf8 failed");
        return false;
    }
    bufLength = bufLength > STRING_LENGTH_MAX ? STRING_LENGTH_MAX : bufLength;
    char str[STRING_LENGTH_MAX] = {0};
    size_t strLen = 0;
    ret = napi_get_value_string_utf8(env, value, str, bufLength + 1, &strLen);
    if (ret != napi_ok) {
        LOGE("napi_get_value_string_utf8 failed");
        return false;
    }
    result = str;
    return true;
}

std::vector<double> GetLevelsArray(const napi_env &env,
                                   const napi_value &value)
{
    if (!IsMatchType(env, value, napi_object)) {
        return std::vector<double>();
    }
    uint32_t array_length;
    napi_status ret = napi_get_array_length(env, value, &array_length);
    if (ret != napi_ok) {
        LOGE("napi_get_array_length failed");
        return std::vector<double>();
    }
    std::vector<double> arr(array_length, 0.0);
    for (uint32_t i = 0; i < array_length; i++) {
        napi_value element = nullptr;
        napi_get_element(env, value, i, &element);
        if (ret != napi_ok) {
            LOGE("napi_get_element failed");
            return std::vector<double>();
        }
        double element_value;
        napi_get_value_double(env, element, &element_value);
        if (ret != napi_ok) {
            LOGE("napi_get_value_double failed");
            return std::vector<double>();
        }
        arr[i] = element_value;
    }
    return arr;
}

napi_value CreateBusinessError(const napi_env &env, const int32_t errCode,
                               const std::string &errMessage)
{
    napi_value businessError = nullptr;
    napi_value code = nullptr;
    napi_value msg = nullptr;
    NAPI_CALL(env, napi_create_int32(env, errCode, &code));
    NAPI_CALL(env, napi_create_string_utf8(env, errMessage.c_str(),
                                           NAPI_AUTO_LENGTH, &msg));
    napi_create_error(env, nullptr, msg, &businessError);
    napi_set_named_property(env, businessError, "code", code);
    return businessError;
}

std::string GetNapiError(int32_t errorCode)
{
    auto iter = ERROR_MESSAGES.find(errorCode);
    if (iter != ERROR_MESSAGES.end()) {
        return iter->second;
    }
    return nullptr;
}

void ThrowErr(const napi_env &env, const int32_t errCode,
              const std::string &printMsg)
{
    LOGE("message: %{public}s, code: %{public}d", printMsg.c_str(), errCode);
    std::string msg = GetNapiError(errCode);
    if (msg.empty()) {
        LOGE("errCode: %{public}d is invalid", errCode);
        return;
    }
    napi_value error = CreateBusinessError(env, errCode, msg);
    napi_throw(env, error);
}

void CreatePixelMap(const napi_env &env, const PixelMap &pixelMap,
                    napi_value &result)
{
    size_t bufferSize = pixelMap.ops.width * pixelMap.ops.height * 4;
    if (pixelMap.buffer == nullptr) {
        LOGE("listener->buffer is null");
        napi_get_undefined(env, &result);
    } else {
        void *buff = malloc(bufferSize);
        memcpy(buff, pixelMap.buffer, bufferSize);
        int32_t ret = OH_PixelMap_CreatePixelMap(env, pixelMap.ops, (uint8_t *)buff,
                                                 bufferSize, &result);
        if (ret != IMAGE_RESULT_SUCCESS || result == nullptr) {
            LOGE("CreatePixelMap failed");
            return;
        }
        NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, result);
        if (native == nullptr) {
            LOGE("native is null");
            return;
        }
        OH_PixelMap_Flip(native, 0, 1);
    }
    return;
}